const { addDefault } = require("@babel/helper-module-imports");
const templates = require('@babel/template').default;

function findParent(path, cond) {
  if (!path || !path.parentPath) {
    return null
  }
  if (cond(path.parentPath)) {
    return path.parentPath;
  } else {
    return findParent(path.parentPath, cond);
  }
}

const myReturnStatementVisitor = {
  "ReturnStatement": function (path) {
    try {
      // assert ReturnStatement is JSXElement
      if (!path.get('argument').isJSXElement())
        return;
      // Builder ImportDeclaration
      var importModule = t.importDeclaration([t.importDeclaration(t.identifier('ErrorBoundary'), t.identifier('ErrorBoundary'))], t.stringLiteral('react-error-boundary'))
      // path.unshiftContainer('body', templates.ast(`import ErrorBoundary from '@liepin/error-boundary;'`));
      this.reactPath.unshiftContainer('body', importModule);
      // }
      // scope crawl
      reactPath.scope.crawl();

      // generate `ErrorBoundary` ReactElement
      var wrap = t.jSXElement(
        t.jSXOpeningElement(t.jSXIdentifier('ErrorBoundary'), []),
        t.jSXClosingElement(t.jSXIdentifier('ErrorBoundary')),
        [path.node.argument]
      );
      path.get('argument').replaceWith(wrap);
    } catch (error) {
      console.error(error)
    }
  }
}

module.exports = function (babel) {
  const { types: t } = babel;
  let isReactComponent = 0;
  return {
    name: "ast-transform", // not required
    visitor: {
      // "ImportDefaultSpecifier": function (path) {
      //   if (path.node.local && path.node.local.name === 'React') {
      //     path.traverse(myReturnStatementVisitor, { reactPath: path });
      //   }
      // }
      "ReturnStatement": function (path) {
        try {
          // assert ReturnStatement is JSXElement
          if (!path.get('argument').isJSXElement())
            return;
          // internal component ignore
          const ignoreJSXElement = ["Tooltip", "InternalMenu", "Menu", "MenuItem", "Switch", "BasicComponent", "Context"];
          if (path.node.argument.openingElement && ignoreJSXElement.includes(path.node.argument.openingElement.name.name)) {
            return;
          }
          // found property 'key', then return
          if (path.get('argument.openingElement.attributes').find(attr => {
            return attr.node.name && attr.node.name.name === 'key';
          })) {
            return;
          }
          const reactPath = findParent(path, function (level) {
            return level.scope.bindings["React"] && level.isProgram();
          })
          if (!reactPath.scope.bindings["ErrorBoundary"]) {
            // Builder ImportDeclaration
            var importModule = t.importDeclaration([t.importSpecifier(t.identifier("ErrorBoundary"), t.identifier("ErrorBoundary"))], t.stringLiteral('@liepin/react-error-boundary'))
            // path.unshiftContainer('body', templates.ast(`import ErrorBoundary from '@liepin/error-boundary;'`));
            reactPath.unshiftContainer('body', importModule);
          }
          // scope crawl
          reactPath.scope.crawl();
    
          // generate `ErrorBoundary` ReactElement
          // var fallback = t.jSXElement(t.jSXOpeningElement(t.jSXIdentifier("div"), []), t.jSXClosingElement(t.jSXIdentifier("div")), [t.jSXText('oh, no')])
          var wrap = t.jSXElement(
            t.jSXOpeningElement(t.jSXIdentifier('ErrorBoundary'), []),
            t.jSXClosingElement(t.jSXIdentifier('ErrorBoundary')),
            [path.node.argument]
          );
          path.get('argument').replaceWith(wrap);
        } catch (error) {
          console.error(error)
        }
      }
    }
  };
}
